package com.ais.gaea.dialect;

import com.ais.gaea.AnnotationUtils;
import com.ais.gaea.ColumnType;
import com.ais.gaea.annotation.TableColumn;
import com.ais.gaea.annotation.TableConstraint;
import com.ais.gaea.annotation.TableIndex;
import com.ais.gaea.constant.Constants;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;


/**
 * @author xuning
 * @date 2022/11/26 11:20:34
 **/

public abstract class DBInstance {
    /**
     * 表存在则进行删除的SQL Statement
     */
    public String dropTableIfExistStatement(Class<?> table) {
        return "DROP TABLE IF EXISTS " + AnnotationUtils.getTableName(table);
    }

    /**
     * 创建表的SQL Statement
     */
    public List<String> createTableStatement(Class<?> table) {
        List<String> list = new ArrayList<>();
        // 创建表结构的sql
        String sb = "CREATE TABLE " +
                AnnotationUtils.getTableName(table) +
                "(\n" +
                columnDefinitions(table) +
                ")" +
                tableOptions(table);
        list.add(sb);
        //给表和列增加注释
        list.addAll(tableAndColumnComment(table));
        //给表增加索引
        list.addAll(createIndexStatement(table));
        return list;
    }

    private List<String> createIndexStatement(Class<?> table) {
        List<String> list = new ArrayList<>();
        TableIndex[] tableIndex = AnnotationUtils.getTableIndex(table);
        for (TableIndex index : tableIndex) {
            StringBuilder sb = new StringBuilder();
            sb.append("CREATE ");
            sb.append(index.type().getType());
            sb.append(' ');
            sb.append(index.name());
            sb.append(" ON ");
            sb.append(AnnotationUtils.getTableName(table));
            sb.append(" (");
            int indexColumnNumber = 0;
            for (String column : index.columns()) {
                if (indexColumnNumber > 0) {
                    sb.append(",");
                }
                sb.append(column);
                indexColumnNumber++;
            }
            sb.append(")");
            list.add(sb.toString());
        }
        return list;
    }


    public String columnDefinitions(Class<?> table) {
        StringBuilder sb = new StringBuilder();
        // 获取所有的column
        TableColumn[] tableColumns = AnnotationUtils.getTableColumns(table);

        int currentColumnIndex = 1;
        for (TableColumn column : tableColumns) {
            if (currentColumnIndex > 1) {
                sb.append(",\n");
            }
            sb.append(columnGenericDefinition(column));
            if (column.primaryKey()) {
                sb.append(getIdentitySql(column));
                sb.append(getNotCluster());
                sb.append(" PRIMARY KEY");
            }
            sb.append(getColumnCommentSql(column));
            currentColumnIndex++;
        }
        //创建约束
        TableConstraint[] constraints = AnnotationUtils.getTableConstraint(table);
        for (TableConstraint constraint : constraints) {
            sb.append(",\n");
            sb.append("CONSTRAINT ");
            sb.append(constraint.name());
            sb.append(' ');
            sb.append(constraint.type().getType());
            boolean first = true;
            for (String column : constraint.columns()) {
                if (first) {
                    sb.append("(");
                    first = false;
                } else {
                    sb.append(",");
                }
                sb.append(column);
            }
            sb.append(")");
        }
        return sb.toString();
    }

    public String columnGenericDefinition(TableColumn column) {
        StringBuilder sb = new StringBuilder();
        sb.append(getColumnName(column.name()));
        sb.append(' ');
        sb.append(getColumnTypeSql(column));

        // 主键可以不指定nullbale属性,主键默认是NOT NULL
        if (column.nullable() && !column.primaryKey()) {
            sb.append(" DEFAULT NULL");
        }
        /* dm数据库primary和not null不能一起使用*/
        else if (!column.primaryKey()) {
            sb.append(" NOT NULL");
            if (!column.defaultValue().isEmpty()) {
                sb.append(getDefaultValueSql(column));
            }
        }
        return sb.toString();
    }


    private String getColumnName(String columnName) {
        if (isReservedWord(columnName)) {
            StringBuilder sb = new StringBuilder();
            sb.append(escapeCharacter(true));
            sb.append(columnName);
            sb.append(escapeCharacter(false));
            columnName = sb.toString();
        }
        return columnName;
    }

    /**
     * 返回创建表可选的配置,即 CREATE TABLE demo(id int not null)之后的内容
     *
     * @param table 表对应的Class对象
     * @return 返回创建表可选的配置
     */
    protected abstract String tableOptions(Class<?> table);


    protected String getDefaultValueSql(TableColumn column) {
        StringBuilder sb = new StringBuilder();
        String defaultValue = column.defaultValue();
        sb.append(" DEFAULT ");
        switch (column.type()) {
            case VARCHAR:
            case TEXT:
                sb.append("'").append(defaultValue).append("'");
                break;
            case DATETIME:
            case DATE:
            case TIME:
                if (Constants.NOW.equalsIgnoreCase(defaultValue)) {
                    ColumnType type = column.type();
                    if (type == ColumnType.DATETIME) {
                        sb.append(Constants.CURRENT_TIMESTAMP);
                    } else if (type == ColumnType.DATE) {
                        sb.append(Constants.CURRENT_DATE);
                    } else if (type == ColumnType.TIME) {
                        sb.append(Constants.CURRENT_TIME);
                    }
                    sb.append("(").append(column.scale()).append(")");
                } else {
                    sb.append("'").append(defaultValue).append("'");
                }
                break;

            case TINYINT_UNSIGNED:
            case TINYINT:
            case SMALLINT:
            case INT:
            case INT_UNSIGNED:
            case BIGINT_UNSIGNED:
            case BIGINT:
            case DECIMAL:
            default:
                sb.append(defaultValue);
        }
        return sb.toString();
    }

    /**
     * 获取列自增的sql
     *
     * @param column 列的属性
     * @return 返回列自增
     */
    protected abstract String getIdentitySql(TableColumn column);


    /**
     * 获取列类型以及长度的sql
     *
     * @param column 列的属性
     * @return 返回列的类型和长度组成的字符串
     */
    protected abstract String getColumnTypeSql(TableColumn column);

    /**
     * 返回列的注释信息
     *
     * @param column 列的属性
     * @return 返回列的注释信息
     */
    protected abstract String getColumnCommentSql(TableColumn column);


    /**
     * 对于Oracle和DM这类数据库，增加注释，不能在创建字段和表的时候增加。需要在这里增加注释。
     *
     * @param table tableClazz
     * @return 返回给表和表中每列增加的comment
     */
    protected List<String> tableAndColumnComment(Class<?> table) {
        return new ArrayList<>();
    }

    protected String getNotCluster() {
        return "";
    }

    /**
     * 返回当前schema下存在所有表的sql
     *
     * @param schemaName schema名字
     * @return 当前schema下所有的表的sql
     */
    public abstract String getExistTableSql(String schemaName);


    /**
     * 获取当前表所有列的属性sql
     *
     * @param schemaName schema名字
     * @param tableName  table名字
     * @return 获取当前表所有列的属性sql
     */
    public abstract String getColumnAttributeSql(String schemaName, String tableName);

    /**
     * 属性名对应的字段名
     *
     * @return 属性名对应的字段名
     */
    public abstract String getField();

    /**
     * 属性类型对应的字段名
     *
     * @return 属性类型对应的字段名
     */
    public abstract String getType();

    /**
     * 属性长度对应的字段名
     *
     * @return 属性长度对应的字段名
     */
    public abstract String getLength();

    /**
     * 属性精度对应的字段名
     *
     * @return 属性精度对应的字段名
     */
    public abstract String getPrecision();

    /**
     * 属性scale对应的字段名
     *
     * @return 属性scale对应的字段名
     */
    public abstract String getScale();

    /**
     * 属性nullable字段名
     *
     * @return 属性nullable字段名
     */
    public abstract String getNullable();

    /**
     * 属性默认值字段名
     *
     * @return 属性默认值字段名
     */
    public abstract String getDefaultValue();

    /**
     * 将字符串nullable转成对应的布尔类型，代表该字段是否为空
     *
     * @param nullable nullable对应的字符串
     * @return 返回是否为空，boolean 是允许空， false不允许为空
     */
    public abstract boolean convertNullable(String nullable);

    /**
     * 将type字符串转换成实际的type，比如在mysql中type字段代表的是类型+长度+精度+标度，需要进行拆分
     *
     * @param type type对应的字符串
     * @return type实际的类型
     */
    public abstract String convertType(String type);

    /**
     * 将length字符串转换成实际的length，比如在mysql中type字段代表的是类型+长度+精度+标度，需要进行拆分
     *
     * @param length length对应的字符串
     * @return length实际的大小
     */
    public abstract String convertLength(String length);

    /**
     * 将precision字符串转换成实际的precision，比如在mysql中type字段代表的是类型+长度+精度+标度，需要进行拆分
     *
     * @param precision precision对应的字符串
     * @return precision实际的大小
     */
    public abstract String convertPrecision(String precision);

    /**
     * 将scale字符串转换成实际的scale，比如在mysql中type字段代表的是类型+长度+精度+标度，需要进行拆分
     *
     * @param scale scale对应的字符串
     * @return scale实际的大小
     */
    public abstract String convertScale(String scale);


    public String convertDefaultValue(String defaultValue, String type) {
        String formatDefaultValue = Optional.ofNullable(defaultValue).orElse(Constants.EMPTY_STRING);
        if (formatDefaultValue.startsWith(Constants.CURRENT_TIMESTAMP)
                || formatDefaultValue.startsWith(Constants.CURRENT_TIME)
                || formatDefaultValue.startsWith(Constants.CURRENT_DATE)) {
            defaultValue = Constants.NOW;
        }
        return defaultValue;
    }

    /**
     * 将ColumnType类型转换成对应数据库的具体类型
     *
     * @param columnType 字段类型
     * @return 返回映射后的类型
     */
    public abstract String mappingColumnType(ColumnType columnType);

    public String updateTableColumnStatement(String tableName, TableColumn tableColumn) {
        return "ALTER TABLE " +
                tableName +
                " MODIFY " +
                columnGenericDefinition(tableColumn);
    }

    public String addTableColumnStatement(String tableName, TableColumn tableColumn) {
        return "ALTER TABLE " +
                tableName +
                " ADD " +
                columnGenericDefinition(tableColumn);
    }

    /**
     * 返回转义字符
     * @param left 转义字符如果为[],需要区分左右边
     * @return 返回转义字符
     */
    public String escapeCharacter(boolean left) {
        return "";
    }

    /**
     * 返回当前字段名是否为保留字
     * @param columnName 原始字段名
     * @return 是否为保留字
     */
    public boolean isReservedWord(String columnName) {
        return false;
    }


    /**
     * 如果已经设置了默认值，应该不会出现说不设置默认值这种情况了，因为如果这样想法，业务sql会需要很大的修改。
     * 所以这里假设不存在这种的情况。我们只考虑如下的情况
     *      1. 没有设置默认值 -> 设置默认值
     *      2. 设置默认值 -> 设置另外一个新的默认值
     */
    public boolean compareDefaultValue(TableColumn tableColumn, String defaultValue) {
        // "NULL" 是达梦数据库在TEXT类型且不设置默认值的时候，返回的不是NULL而是“NULL”,怀疑DM驱动做了修改或者是个BUG
        if (ObjectUtils.isEmpty(defaultValue) || defaultValue.equalsIgnoreCase(Constants.NULL_STRING)) {
            //这种情况是当前数据库的默认值是没有设置
            //当前tableColumn的默认值是否有设置，如果没有设置，说明没有发生变化。
            //当前tableColumn的默认值设置了，比较一下是不是设置了"NULL"这个默认值，当前这个判断只是规避极端的情况
            return !ObjectUtils.isEmpty(tableColumn.defaultValue()) && tableColumn.defaultValue().equalsIgnoreCase(defaultValue);
        }
        // 如下情况是设置默认值 -> 设置另外一个新的默认值
        try{
            switch (tableColumn.type()) {
                case TINYINT:
                case TINYINT_UNSIGNED:
                case SMALLINT:
                case INT:
                case INT_UNSIGNED:
                case BIGINT:
                case BIGINT_UNSIGNED:
                    // 整数，直接转成Long来比较
                    return Long.parseLong(tableColumn.defaultValue()) == Long.parseLong(defaultValue);
                case DECIMAL:
                    // 定点数，转成BigDecimal来比较
                    return new BigDecimal(tableColumn.defaultValue()).equals(new BigDecimal(defaultValue));
                default:
                    return tableColumn.defaultValue().equalsIgnoreCase(defaultValue);
            }
        } catch (Exception ex) {
            // do nothing, return true
        }
        return true;
    }
}
